package com.bigknow.appstore.proxy.handler.base;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.cache.IProxyResourceMetaCache;
import com.bigknow.appstore.proxy.entity.ProxyResourceMeta;
import com.bigknow.appstore.proxy.enumerate.ErrorType;
import com.bigknow.appstore.proxy.enumerate.ProxyState;
import com.bigknow.appstore.proxy.exceptions.ProxyError;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorAssert;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorHelper;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.util.UriAndUrlHelper;
import com.bigknow.appstore.proxy.vo.RequestVo;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.bigknow.appstore.common.Assert.assertNotNull;
import static com.bigknow.appstore.common.Assert.assertNotNullString;

/**
 * 基础outbound类
 * Created by chaos on 2016/9/20.
 */
public abstract class BaseOutboundHandler extends ChannelOutboundHandlerAdapter {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    protected IProxyServer proxyServer;

    protected ICacheManager cacheManager;

    protected ILogWriter writer;

    public BaseOutboundHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        assertNotNull(proxyServer);
        assertNotNull(writer);
        assertNotNull(cacheManager);
        this.writer = writer;
        this.proxyServer = proxyServer;
        this.cacheManager = cacheManager;
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        try {

            /**
             * 对于server端来说 fullHttpMessage就是 fullHttpRequest
             * 对于client端来说 fullHttpMessage就是 fullHttpResponse
             * 所以这里只能判断是否是fullHttpMsg，判断是否是正确的fullHttpRequest 或者 fullHttpResponse需要在
             * 具体实现类中判断
             */
            ErrorAssert.assertFullHttpMsg(ctx, msg, getRequestVo(ctx));
            String proxyError = ((FullHttpMessage) msg).headers().get("proxyError");
            //如果消息头带有proxyError，说明是系统发出的错误信息，不处理，直接过
            if (proxyError != null) {
                ctx.write(msg, promise);
            } else {
                this.write(ctx, (FullHttpMessage) msg, promise);
            }
        } catch (ProxyError proxyError) {
            //统一的错误处理
            if (proxyError.getRequestVo() == null) {
                proxyError.setRequestVo(getRequestVo(ctx));
            }
            proxyServer.getErrorProcessor().processError(ctx, proxyError);
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (msg instanceof FullHttpRequest) {
                    ErrorHelper.throwError(ctx.channel(), getRequestVo(ctx), ErrorType.OTHER_ERROR, e.getMessage()
                            , msg.toString(), null);
                } else if (msg instanceof FullHttpResponse) {
                    ErrorHelper.throwError(ctx.channel(), getRequestVo(ctx), ErrorType.OTHER_ERROR, e.getMessage()
                            , null, null);
                } else {
                    ErrorHelper.throwError(ctx.channel(), getRequestVo(ctx), ErrorType.OTHER_ERROR, e.getMessage()
                            , null, null);
                }
            } catch (ProxyError proxyError) {
                proxyServer.getErrorProcessor().processError(ctx, proxyError);
            }
        }
    }

    public abstract void write(ChannelHandlerContext ctx, FullHttpMessage msg, ChannelPromise promise);

    protected void closeConnection(Channel channel) {
        if (channel == null) return;
        channel.close().awaitUninterruptibly();
    }

    public IProxyResourceMetaCache getProxyResourceCache() {
        return cacheManager.getProxyResCache(proxyServer.getProxyId());
    }

    /**
     * 通过ctx来获取本次response对应的request请求的requestVo
     *
     * @param ctx
     * @return
     */
    public RequestVo getRequestVo(ChannelHandlerContext ctx) {
        return HandlerVariables.requestVoForCurrentResponse.get(ctx.channel().id().asLongText());
    }

    /**
     * 查找并生成新的路径，如果没有查找到就返回为空
     *
     * @param url
     * @return
     */
    protected String findAndCreateCurrentUrl(String url) {
        ProxyResourceMeta resourceMeta = getProxyResourceCache().getProxyResourceMetaBySourceUrl(url);
        if (resourceMeta == null) return null;
        StringBuilder builder = new StringBuilder();
        builder.append(proxyServer.getProxyMeta().isHTTPS() ? "https://" : "http://").append(proxyServer.getRealServerIp())
                .append(":")
                .append(proxyServer.getProxyMeta().getPort())
                .append(resourceMeta.getProxyUri())
                .append(url.substring(UriAndUrlHelper.getStandardUrl(resourceMeta.getSourceUrl()).length(), url.length()));
        return builder.toString();
    }

}
